package geerpc

import (
	"go/ast"
	"log"
	"reflect"
	"sync/atomic"
)

// 每个methodType实例包含一个方法的完整信息
type methodType struct {
	method    reflect.Method  // 方法本身
	ArgType   reflect.Type  // 第一个参数的类型
	ReplyType reflect.Type  // 第二个参数的类型
	numCalls  uint64  // 后续统计方法调用次数时会用到
}


// 获取方法调用次数，用原子操作保证数据准确性
func (m *methodType) NumCalls() uint64 {
	return atomic.LoadUint64(&m.numCalls)
}

// 创建第一个参数类型对应的实例
func (m *methodType) newArgv() reflect.Value {
	var argv reflect.Value
	// arg may be a pointer type, or a value type
	if m.ArgType.Kind() == reflect.Ptr {
		argv = reflect.New(m.ArgType.Elem())
	} else {
		argv = reflect.New(m.ArgType).Elem()
	}
	return argv
}

// 创建第二个参数对应的实例
func (m *methodType) newReplyv() reflect.Value {
	// reply must be a pointer type
	replyv := reflect.New(m.ReplyType.Elem())
	switch m.ReplyType.Elem().Kind() {
	case reflect.Map:
		replyv.Elem().Set(reflect.MakeMap(m.ReplyType.Elem()))
	case reflect.Slice:
		replyv.Elem().Set(reflect.MakeSlice(m.ReplyType.Elem(), 0, 0))
	}
	return replyv
}


// 一个service实例包含了一个结构体所有信息
type service struct {
	name   string  // 映射结构体的名称
	typ    reflect.Type  // 结构体的类型
	rcvr   reflect.Value  // 结构体的实例本身，保留rcvr主要是调用时需要rcvr作为第0个参数
	method map[string]*methodType  // 存储映射的结构体的所有符合条件的方法
}

// 创建一个service
func newService(rcvr interface{}) *service {
	s := new(service)
	s.rcvr = reflect.ValueOf(rcvr)
	s.name = reflect.Indirect(s.rcvr).Type().Name()
	s.typ = reflect.TypeOf(rcvr)
	if !ast.IsExported(s.name) {
		log.Fatalf("rpc server: %s is not a valid service name", s.name)
	}
	s.registerMethods()
	return s
}

// 为service的method map填充具体值
func (s *service) registerMethods() {
	s.method = make(map[string]*methodType)  // 初始化map
	for i := 0; i < s.typ.NumMethod(); i++ {  // 根据s的方法个数来进行遍历
		method := s.typ.Method(i)  // 方法实例
		mType := method.Type  // 方法的Type
		if mType.NumIn() != 3 || mType.NumOut() != 1 {  // 如果方法参数个数不等于3 或者方法的返回值个数不等于1
			continue
		}
		if mType.Out(0) != reflect.TypeOf((*error)(nil)).Elem() {  // 如果返回参数的类型不为error
			continue
		}
		argType, replyType := mType.In(1), mType.In(2)  // 得到argType 与 replyType
		if !isExportedOrBuiltinType(argType) || !isExportedOrBuiltinType(replyType) { // 如果不为导出或者内建类型
			continue
		}
		s.method[method.Name] = &methodType{  // 将得到的当前methodType实例信息存入map
			method:    method,
			ArgType:   argType,
			ReplyType: replyType,
		}
		log.Printf("rpc server: register %s.%s\n", s.name, method.Name)
	}
}

// 通过反射调用方法
func (s *service) call(m *methodType, argv, replyv reflect.Value) error {
	atomic.AddUint64(&m.numCalls, 1)
	f := m.method.Func
	returnValues := f.Call([]reflect.Value{s.rcvr, argv, replyv})
	if errInter := returnValues[0].Interface(); errInter != nil {
		return errInter.(error)
	}
	return nil
}

func isExportedOrBuiltinType(t reflect.Type) bool {
	return ast.IsExported(t.Name()) || t.PkgPath() == ""
}
